package com.spzx.cart.service.impl;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.spzx.cart.api.domain.CartInfo;
import com.spzx.cart.service.ICartService;
import com.spzx.common.core.constant.SecurityConstants;
import com.spzx.common.core.context.SecurityContextHolder;
import com.spzx.common.core.domain.R;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.common.security.utils.SecurityUtils;
import com.spzx.product.api.RemoteProductService;
import com.spzx.product.api.vo.ProductSkuVo;
import com.spzx.product.api.vo.SkuPriceVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class CartServiceImpl implements ICartService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RemoteProductService remoteProductService;

    private String getCartKey(Long userId) {
        //定义key user:userId:cart
        return "user:"+ userId +":cart" ;
    }

    /**
     * 将指定商品添加到当前用户的购物车中
     *
     * @param skuId 商品SKU ID，用于标识特定的商品
     * @param skuNum 要添加的商品数量
     */
    @Override
    public void addToCart(Long skuId, Integer skuNum) {
        // 获取当前登录用户的ID
        Long userId = SecurityContextHolder.getUserId();

        // 构建“用户”购物车的Hash结构Key，格式为 user:用户ID:cart
        String cartKey = getCartKey(userId);

        // 创建Hash结构绑定操作对象，便于对Hash进行操作
        BoundHashOperations<String, String, CartInfo> hashOps = redisTemplate.boundHashOps(cartKey);

        // 判断用户购物车中是否包含该商品
        // 如果包含：数量进行累加（某件商品数量上限为99）
        // 如果不包含：新增购物车商品
        String hashKey = skuId.toString();
        Integer threshold = 99;
        if (Boolean.TRUE.equals(hashOps.hasKey(hashKey))) {
            // 该商品在购物车中已有，对数量进行累加，不能超过指定上限99
            // 从Redis中获取购物车信息
            CartInfo cartInfo = hashOps.get(hashKey);
            // 计算购物车中商品的总数量，确保不会超过阈值
            int totalCount = Objects.requireNonNull(cartInfo).getSkuNum() + skuNum;
            // 更新购物车中商品的数量
            cartInfo.setSkuNum(totalCount > threshold ? threshold : totalCount);
            // 更新Redis中的购物车信息
            hashOps.put(hashKey, cartInfo);

        } else {
            // 判断购物车商品种类（不同SKU）总数是否大于50件
            Long count = hashOps.size();
            if (++count > 50) {
                throw new RuntimeException("商品种类数量超过上限！");
            }

            // 购物车没有该商品，构建购物车对象，存入Redis
            CartInfo cartInfo = new CartInfo();
            cartInfo.setUserId(userId);
            cartInfo.setSkuNum(skuNum > threshold ? threshold : skuNum);

            // 远程调用商品服务获取商品SKU基本信息
            R<ProductSkuVo> productSkuResult = remoteProductService.getProductSku(skuId, SecurityConstants.INNER);
            if (R.FAIL == productSkuResult.getCode()) {
                throw new ServiceException(productSkuResult.getMsg());
            }
            ProductSkuVo productSku = productSkuResult.getData();
            cartInfo.setSkuId(skuId);
            cartInfo.setSkuName(productSku.getSkuName());
            cartInfo.setThumbImg(productSku.getThumbImg());
            cartInfo.setCartPrice(productSku.getSalePrice());
            cartInfo.setSkuPrice(productSku.getSalePrice());
            cartInfo.setCreateTime(new Date());

            // 将购物车商品存入Redis
            hashOps.put(hashKey, cartInfo);
        }
    }

    /**
     * 重写获取购物车列表的方法
     * 此方法从Redis中获取当前用户购物车中的商品信息列表，并按创建时间降序排序
     *
     * @return 返回购物车信息列表，如果购物车为空，则返回空列表
     */
    @Override
    public List<CartInfo> getCartList() {
        // 获取当前登录用户的id
        Long userId = SecurityContextHolder.getUserId();
        String cartKey = this.getCartKey(userId);
        // 从Redis中获取购物车列表信息
        List<CartInfo> cartInfoList = redisTemplate.opsForHash().values(cartKey);
        if (!CollectionUtils.isEmpty(cartInfoList)) {
            return cartInfoList
                    //开始流操作
                    .stream()
                    //中间操作：升序排序
                    .sorted((o1, o2) -> o2.getCreateTime().compareTo(o1.getCreateTime()))
                    //终结操作
                    .collect(Collectors.toList());
        }
        return new ArrayList<>();
    }

    /**
     * 删除购物车中的指定商品
     *
     * @param skuId 商品SKU ID，用于标识购物车中的具体商品
     *              此方法首先获取当前登录用户的ID，然后根据用户ID获取购物车在Redis中的键
     *              接着检查购物车中是否存在指定的商品，如果不存在则抛出异常
     *              如果存在，则从购物车中删除该商品的信息
     */
    @Override
    public void deleteCart(Long skuId) {
        // 获取当前登录用户的id
        Long userId = SecurityContextHolder.getUserId();
        String cartKey = this.getCartKey(userId);

        //获取缓存数据
        BoundHashOperations<String, String, CartInfo> boundHashOperations = redisTemplate.boundHashOps(cartKey);

        if (Boolean.FALSE.equals(boundHashOperations.hasKey(skuId.toString()))) {
            throw new RuntimeException("购物车中没有该商品！");
        }
        boundHashOperations.delete(skuId.toString());

    }

    /**
     * 更新购物车中指定商品的选中状态
     *
     * @param skuId 商品ID，用于标识购物车中的商品
     * @param isChecked 商品的选中状态，1表示选中，0表示未选中
     */
    @Override
    public void checkCart(Long skuId, Integer isChecked) {
        // 获取当前登录用户的id
        Long userId = SecurityContextHolder.getUserId();
        // 获取购物车key
        String cartKey = this.getCartKey(userId);
        // 从缓存中获取购物车数据
        BoundHashOperations<String, String, CartInfo> boundHashOperations = redisTemplate.boundHashOps(cartKey);
        // 判断购物车中是否包含该商品
        if (Boolean.TRUE.equals(boundHashOperations.hasKey(skuId.toString()))) {
            // 获取购物车中商品的信息
            CartInfo cartInfo = boundHashOperations.get(skuId.toString());
            // 如果 cartInfo 为 null，则抛出 NullPointerException，否则更新购物车中商品的状态
            Objects.requireNonNull(cartInfo).setIsChecked(isChecked);
            // 更新缓存
            boundHashOperations.put(skuId.toString(), cartInfo);
        }

    }

    /**
     * 全选或取消全选购物车中的商品
     *
     * @param isChecked 表示购物车中商品的选中状态，1为选中，0为未选中
     */
    @Override
    public void allCheckCart(Integer isChecked) {
        // 获取当前登录用户的id
        Long userId = SecurityContextHolder.getUserId();
        // 获取购物车key
        String cartKey = this.getCartKey(userId);
        // 从缓存中获取购物车数据
        BoundHashOperations<String, String, CartInfo> boundHashOperations = redisTemplate.boundHashOps(cartKey);
        // 获取购物车列表
        List<CartInfo> cartInfoList = boundHashOperations.values();
        // 判断购物车列表是否为空
        if (!CollectionUtils.isEmpty(cartInfoList)) {
            // 遍历购物车列表，更新购物车中商品的状态
            cartInfoList.forEach(cartInfo -> {
                // 获取购物车中对应商品的详细信息
                CartInfo cartInfoUpd = boundHashOperations.get(cartInfo.getSkuId().toString());
                // 如果 cartInfoUpd 为 null，则抛出 NullPointerException，否则更新购物车中商品的状态
                Objects.requireNonNull(cartInfoUpd).setIsChecked(isChecked);
                // 更新缓存
                boundHashOperations.put(cartInfo.getSkuId().toString(), cartInfoUpd);
            });
        }
    }

    @Override
    public void clearCart() {
        // 获取当前登录用户的id
        Long userId = SecurityContextHolder.getUserId();
        String cartKey = getCartKey(userId);
        // 删除购物车
        redisTemplate.delete(cartKey);
    }

    /**
     * 获取用户已勾选的商品列表
     * 本方法从Redis缓存中获取用户的购物车信息，并筛选出用户已勾选的商品
     * 使用了SecurityContextHolder来获取当前用户的ID，以便于获取对应的购物车信息
     *
     * @return 返回用户已勾选的商品列表如果购物车为空或没有商品被勾选，则返回空列表
     */
    @Override
    public List<CartInfo> getCartCheckedList() {
        // 获取当前用户的ID
        Long userId = SecurityUtils.getLoginUser().getUserid();
        // 根据用户ID获取购物车在Redis中的键
        String cartKey = this.getCartKey(userId);
        // 从Redis中获取购物车中的所有商品信息
        List<CartInfo> cartCachInfoList = redisTemplate.opsForHash().values(cartKey);
        // 初始化将要返回的商品列表
        List<CartInfo> cartInfoList = new ArrayList<>();
        // 如果从Redis中获取的商品信息不为空
        if (!CollectionUtils.isEmpty(cartCachInfoList)) {
            // 筛选出用户已勾选的商品（isChecked为1表示已勾选）
            cartInfoList = cartCachInfoList
                    //开始流操作
                    .stream()
                    // 过滤出isChecked为1的元素
                    .filter(item -> item.getIsChecked() == 1)
                    // 终结操作
                    .collect(Collectors.toList());
        }
        // 返回已勾选的商品列表
        return cartInfoList;
    }

    @Override
    public Boolean updateCartPrice(Long userId) {
        // 获取购物车key
        String cartKey = getCartKey(userId);
        // 从缓存中获取购物车数据
        BoundHashOperations<String, String, CartInfo> hashOperations = redisTemplate.boundHashOps(cartKey);
        // 获取购物车列表
        List<CartInfo> cartCachInfoList = hashOperations.values();
        if (!CollectionUtils.isEmpty(cartCachInfoList)) {
            cartCachInfoList.forEach(cartInfo -> {
                // 判断是否选中
                if (cartInfo.getIsChecked().intValue() == 1) {
                    // 远程调用获取实时价格
                    SkuPriceVo skuPrice = remoteProductService
                            .getSkuPrice(cartInfo.getSkuId(), SecurityConstants.INNER).getData();
                    //放入购物车时价格
                    cartInfo.setCartPrice(skuPrice.getSalePrice());
                    //实时价格
                    cartInfo.setSkuPrice(skuPrice.getSalePrice());
                    // 更新缓存
                    hashOperations.put(cartInfo.getSkuId().toString(), cartInfo);
                }

            });
        }
        return true;
    }

    @Override
    public Boolean deleteCartCheckedList(Long userId) {
        //获取购物车key
        String cartKey = getCartKey(userId);
        //获取缓存中的购物车数据
        BoundHashOperations<String, String, CartInfo> hashOperations = redisTemplate.boundHashOps(cartKey);
        //获取购物车列表
        List<CartInfo> cartCachInfoList = hashOperations.values();
        //判断购物车列表是否为空
        if (!CollectionUtils.isEmpty(cartCachInfoList)) {
            cartCachInfoList.forEach(cartInfo -> {
                if (cartInfo.getIsChecked() == 1) {
                    //删除购物车中的商品
                    hashOperations.delete(cartInfo.getSkuId().toString());
                }
            });
        }

        return null;
    }


}